08/05/2020

library(tidyr)

Attaching package: 㤼㸱tidyr㤼㸲

The following object is masked from 㤼㸱package:magrittr㤼㸲:

    extract
wd <- getwd()
path1 <- file.path(wd, "Homo Deus_ A Brief History of T - Yuval Noah Harari.txt")
path2 <- file.path(wd,"Sapiens_ A Brief History of Hum - Yuval Noah Harari.txt")
path3 <- file.path(wd, "21 Lessons for the 21st Century - Yuval Noah Harari.txt")

homo <- readtext(path1)  
sapiens <- readtext(path2)
lessons <- readtext(path3)

yuval_books <- bind_rows(homo, sapiens, lessons)
yuval_books %<>% mutate(book = case_when(doc_id == "Homo Deus_ A Brief History of T - Yuval Noah Harari.txt" ~ "homo_dues",
                                         doc_id == "Sapiens_ A Brief History of Hum - Yuval Noah Harari.txt" ~ "sapiens",
                                         doc_id == "21 Lessons for the 21st Century - Yuval Noah Harari.txt" ~ "21_lessons")) %>%
  select(-doc_id)
  

head(yuval_books)
NA
yuval_bigram <- yuval_books %>% unnest_tokens(bigram, text, token = "ngrams", n = 2)
bigram_separated <- yuval_bigram %>% separate(bigram, c("word1", "word2"), sep = " ")

bigram_filtered <- bigram_separated %>% filter(!word1 %in% stop_words$word) %>%
  filter(!word2 %in% stop_words$word)

bigram_counted <- bigram_filtered %>%
  count(word1, word2, sort = TRUE)
bigram_counted
bigram_united <- bigram_filtered %>% unite(bigram, word1, word2, sep = " ")
bigram_united
trigram_filtered <- yuval_books %>%
  unnest_tokens(trigram, text, token = "ngrams", n = 3) %>%
  separate(trigram, c("word1", "word2", "word3"), sep = " ") %>%
  filter(!word1 %in% stop_words$word,
         !word2 %in% stop_words$word,
         !word3 %in% stop_words$word)

trigram_filtered %>% count(word1, word2, word3, sort = TRUE)

trigram_united <- trigram_filtered %>% unite(trigram, word1, word2, word3, sep = " ")

trigram_united
trigram_filtered %>%
  filter(word2 == "future") %>%
  count(book, word1, sort = TRUE)
trigram_filtered %>%
  filter(word2 == "past") %>%
  count(book, word1, sort = TRUE)
monogram_filtered <- yuval_books %>%
  unnest_tokens(monogram, text, token = "ngrams", n = 1) %>%
  filter(!monogram %in% stop_words$word)
monogram_filtered %>%
  filter(monogram == "future") %>%
  count(book, monogram, sort = TRUE)
bigram_tf_idf <- bigram_united %>%
  count(book, bigram) %>%
  bind_tf_idf(bigram, book, n) %>%
  arrange(desc(tf_idf))

bigram_tf_idf
trigram_tf_idf <- trigram_united %>%
  count(book, trigram) %>%
  bind_tf_idf(trigram, book, n) %>%
  arrange(desc(tf_idf))

trigram_tf_idf
bigram_separated %>%
  filter(word1 == "not") %>%
  count(word1, word2, sort = TRUE)
AFINN <- get_sentiments("afinn")

AFINN
not_words <- bigram_separated %>%
  filter(word1 == "not") %>%
  inner_join(AFINN, by = c(word2 = "word")) %>%
  count(word2, value, sort = TRUE)

not_words
library(ggplot2)

not_words %>%
  mutate(contribution = n * value) %>%
  arrange(desc(abs(contribution))) %>%
  head(20) %>%
  mutate(word2 = reorder(word2, contribution)) %>%
  ggplot(aes(word2, n * value, fill = n * value > 0)) +
  geom_col(show.legend = FALSE) +
  xlab("Words preceded by \"not\"") +
  ylab("Sentiment value * number of occurrences") +
  coord_flip()

# filter for only relatively common combinations
bigram_graph <- bigram_counted %>%
  filter(n > 20) %>%
  graph_from_data_frame()

bigram_graph
IGRAPH 56d3872 DN-- 74 48 -- 
+ attr: name (v/c), n (e/n)
+ edges from 56d3872 (vertex names):
 [1] homo        ->sapiens      twentieth   ->century      agricultural->revolution   human       ->rights       nineteenth  ->century     
 [6] data        ->processing   economic    ->growth       middle      ->east         natural     ->selection    world       ->war         
[11] hunter      ->gatherers    free        ->market       industrial  ->revolution   hunter      ->gatherer     global      ->warming     
[16] artificial  ->intelligence cognitive   ->revolution   million     ->people       entire      ->world        human       ->species     
[21] stone       ->age          modern      ->science      world       ->view         scientific  ->revolution   climate     ->change      
[26] science     ->fiction      subjective  ->experiences  famine      ->plague       human       ->brain        vast        ->majority    
[31] liberal     ->story        roman       ->empire       secular     ->people       job         ->market       human       ->life        
[36] raw         ->materials    genetic     ->engineering  scientific  ->research     â           ->â            ancient     ->foragers    
+ ... omitted several edges
library(ggraph)
package 㤼㸱ggraph㤼㸲 was built under R version 3.6.3
set.seed(2017)

ggraph(bigram_graph, layout = "fr") +
  geom_edge_link() +
  geom_node_point() +
  geom_node_text(aes(label = name), vjust = 1, hjust = 1)

set.seed(2016)

a <- grid::arrow(type = "closed", length = unit(.15, "inches"))

ggraph(bigram_graph, layout = "fr") +
  geom_edge_link(aes(edge_alpha = n), show.legend = FALSE,
                 arrow = a, end_cap = circle(.07, 'inches')) +
  geom_node_point(color = "lightblue", size = 5) +
  geom_node_text(aes(label = name), vjust = 1, hjust = 1) +
  theme_void()

29/04/2020 - 07/05/2020
r library(quanteda) library(readtext) library(tidytext) library(tidyverse)
```r #path1 <- “C:\Users\E7470\Calibre Library\Yuval Noah Harari\Homo Deus_ A Brief History of Tomor (663)\Homo Deus_ A Brief History of T - Yuval Noah Harari.txt” #path2 <- “C:\Users\E7470\Calibre Library\Yuval Noah Harari\Sapiens_ A Brief History of Humanki (353)\Sapiens_ A Brief History of Hum - Yuval Noah Harari.txt” #path3 <- “C:\Users\E7470\Calibre Library\Yuval Noah Harari\21 Lessons for the 21st Century (664)\21 Lessons for the 21st Century - Yuval Noah Harari.txt”
path1 <- “C:\Tran Phu Hoa workspace\1. Current\1_NLP\Natural-Language-Processing\Homo Deus_ A Brief History of T - Yuval Noah Harari.txt” path2 <- “C:\Tran Phu Hoa workspace\1. Current\1_NLP\Natural-Language-Processing\Sapiens_ A Brief History of Hum - Yuval Noah Harari.txt” path3 <- “C:\Tran Phu Hoa workspace\1. Current\1_NLP\Natural-Language-Processing\21 Lessons for the 21st Century - Yuval Noah Harari.txt”
homo_deus <- readtext(path1) sapiens <- readtext(path2) lessons <- readtext(path3)
yuval_books <- bind_rows(homo_deus, sapiens, lessons)
```
r tidy_yuval <- yuval_books %>% unnest_tokens(word,text)
r tidy_yuval %>% anti_join(stop_words) %>% count(doc_id,word, sort = TRUE) %>% bind_tf_idf(word,doc_id,n) %>% select(-doc_id) %>% arrange(desc(tf_idf))
```r #mystopwords <- tibble(word = c(“www.â”, “2017”, “2016”, “cortã”, “2015”, “bn”, “file”, “cg”, “cb”, “cm”, “ab”, "_k“,”k“,”_x"))
#physics_words <- anti_join(physics_words, mystopwords, by = “word”)
plot_yuval <- tidy_yuval %>% anti_join(stop_words) %>% count(doc_id,word, sort = TRUE) %>% bind_tf_idf(word,doc_id,n) %>% mutate(word = str_remove_all(word, “")) %>% group_by(doc_id) %>% top_n(15, tf_idf) %>% ungroup() %>% mutate(word = reorder_within(word, tf_idf, doc_id)) %>% mutate(author = factor(doc_id, levels = c("Homo Deus A Brief History of T - Yuval Noah Harari.txt”, “21 Lessons for the 21st Century - Yuval Noah Harari.txt”, “Sapiens_ A Brief History of Hum - Yuval Noah Harari.txt”)))
ggplot(plot_yuval, aes(word, tf_idf, fill = doc_id)) + geom_col(show.legend = FALSE) + labs(x = NULL, y = “tf-idf”) + facet_wrap(~doc_id, ncol = 2, scales = “free”) + coord_flip() + scale_x_reordered() ```
r yuval_books %>% filter(str_detect(text, "2017")) %>% select(text)
```r library(forcats)
plot_physics <- physics_words %>% bind_tf_idf(word, author, n) %>% mutate(word = fct_reorder(word, tf_idf)) %>% mutate(author = factor(author, levels = c(“Galilei, Galileo”, “Huygens, Christiaan”, “Tesla, Nikola”, “Einstein, Albert”))) ```
```r
plot_physics %>% group_by(author) %>% top_n(15, tf_idf) %>% ungroup() %>% mutate(word = reorder(word, tf_idf)) %>% ggplot(aes(word, tf_idf, fill = author)) + geom_col(show.legend = FALSE) + labs(x = NULL, y = “tf-idf”) + facet_wrap(~author, ncol = 2, scales = “free”) + coord_flip() ```

28/04/2020

Chapter 3 We want to answer how to quantify what a document is about

To answer these questions we test this method: tf = term frequency idf = inverse document frequency = decreases the weight for commonly used words and increases the weight for words that are not used very much in a collection of documents

tf - idf = The statistic tf-idf is intended to measure how important a word is to a document in a collection (or corpus) of documents, for example, to one novel in a collection of novels or to one website in a collection of websites.

library(tidyverse)
library(janeaustenr)
library(tidytext)
library(textdata)

book_words <- austen_books() %>%
  unnest_tokens(word, text) %>%
  count(book, word, sort = TRUE)

total_words <- book_words %>% 
  group_by(book) %>% 
  summarize(total = sum(n))

book_words <- left_join(book_words, total_words)
book_words

Zipf’s law states that the frequency that a word appears is inversely proportional to its rank.

freq_by_rank <- book_words %>% 
  group_by(book) %>% 
  mutate(rank = row_number(), 
         `term frequency` = n/total)

freq_by_rank
freq_by_rank %>% 
  ggplot(aes(rank, `term frequency`, color = book)) + 
  geom_line(size = 1.1, alpha = 0.8, show.legend = FALSE) + 
  scale_x_log10() +
  scale_y_log10()
rank_subset <- freq_by_rank %>% 
  filter(rank < 500,
         rank > 10)

lm(log10(`term frequency`) ~ log10(rank), data = rank_subset)
freq_by_rank %>% 
  ggplot(aes(rank, `term frequency`, color = book)) + 
  geom_abline(intercept = -0.62, slope = -1.1, color = "gray50", linetype = 2) +
  geom_line(size = 1.1, alpha = 0.8, show.legend = FALSE) + 
  scale_x_log10() +
  scale_y_log10()

The idea of tf-idf is to find the important words for the content of each document by decreasing the weight for commonly used words and increasing the weight for words that are not used very much in a collection or corpus of documents, in this case, the group of Jane Austen’s novels as a whole. Calculating tf-idf attempts to find the words that are important (i.e., common) in a text, but not too common

book_words <- book_words %>%
  bind_tf_idf(word, book, n)

book_words
book_words %>%
  select(-total) %>%
  arrange(desc(tf_idf))
book_words %>%
  arrange(desc(tf_idf)) %>%
  mutate(word = factor(word, levels = rev(unique(word))))
book_words %>%
  arrange(desc(tf_idf)) %>%
  mutate(word = factor(word, levels = rev(unique(word)))) %>% 
  group_by(book) %>% 
  top_n(15) %>% 
  ungroup()
book_words %>%
  arrange(desc(tf_idf)) %>%
  mutate(word = factor(word, levels = rev(unique(word)))) %>% 
  group_by(book) %>% 
  top_n(15) %>% 
  ungroup() %>%
  ggplot(aes(word, tf_idf, fill = book)) +
  geom_col(show.legend = FALSE) +
  labs(x = NULL, y = "tf-idf") +
  facet_wrap(~book, ncol = 2, scales = "free") +
  coord_flip()

Still all proper nouns in Figure 3.4! These words are, as measured by tf-idf, the most important to each novel and most readers would likely agree. What measuring tf-idf has done here is show us that Jane Austen used similar language across her six novels, and what distinguishes one novel from the rest within the collection of her works are the proper nouns, the names of people and places. This is the point of tf-idf; it identifies words that are important to one document within a collection of documents.

suppressMessages({
  if(!require(gutenbergr))
    install.packages("gutenbergr", repos = "http://cran.us.r-project.org")
  library(gutenbergr)
  
})
physics <- gutenberg_download(c(37729, 14725, 13476, 30155), 
                              meta_fields = "author")

To calculate and visulize relationships between words in my text dataset

Tokenizing by n-gram

We do this by adding the token = “ngrams” option to unnest_tokens(), and setting n to the number of words we wish to capture in each n-gram. When we set n to 2, we are examining pairs of two consecutive words, often called “bigrams”:

austen_bigrams <- austen_books() %>%
  unnest_tokens(bigram, text, token = "ngrams", n = 2)

austen_bigrams

Counting and filtering n-grams

austen_bigrams %>%
  count(bigram, sort = TRUE)

As one might expect, a lot of the most common bigrams are pairs of common (uninteresting) words, such as of the and to be: what we call “stop-words” (see Chapter 1). This is a useful time to use tidyr’s separate(), which splits a column into multiple based on a delimiter. This lets us separate it into two columns, “word1” and “word2”, at which point we can remove cases where either is a stop-word.

bigrams_separated <- austen_bigrams %>%
  separate(bigram, c("word1", "word2"), sep = " ")

bigrams_filtered <- bigrams_separated %>%
  filter(!word1 %in% stop_words$word) %>%
  filter(!word2 %in% stop_words$word)

# new bigram counts:
bigram_counts <- bigrams_filtered %>% 
  count(word1, word2, sort = TRUE)

bigram_counts
bigrams_united <- bigrams_filtered %>%
  unite(bigram, word1, word2, sep = " ")

bigrams_united

In other analyses you may be interested in the most common trigrams, which are consecutive sequences of 3 words. We can find this by setting n = 3:

austen_books() %>%
  unnest_tokens(trigram, text, token = "ngrams", n = 3) %>%
  separate(trigram, c("word1", "word2", "word3"), sep = " ") %>%
  filter(!word1 %in% stop_words$word,
         !word2 %in% stop_words$word,
         !word3 %in% stop_words$word) %>%
  count(word1, word2, word3, sort = TRUE)
bigrams_filtered %>%
  filter(word2 == "street") %>%
  count(book, word1, sort = TRUE)

A bigram can also be treated as a term in a document in the same way that we treated individual words. For example, we can look at the tf-idf (Chapter 3) of bigrams across Austen novels. These tf-idf values can be visualized within each book, just as we did for words (Figure 4.1).

bigram_tf_idf <- bigrams_united %>%
  count(book, bigram) %>%
  bind_tf_idf(bigram, book, n) %>%
  arrange(desc(tf_idf))

bigram_tf_idf
not_words <- bigrams_separated %>%
  filter(word1 == "not") %>%
  inner_join(AFINN, by = c(word2 = "word")) %>%
  count(word2, value, sort = TRUE)

not_words
not_words %>%
  mutate(contribution = n * value) %>%
  arrange(desc(abs(contribution))) %>%
  head(20) %>%
  mutate(word2 = reorder(word2, contribution)) %>%
  ggplot(aes(word2, n * value, fill = n * value > 0)) +
  geom_col(show.legend = FALSE) +
  xlab("Words preceded by \"not\"") +
  ylab("Sentiment value * number of occurrences") +
  coord_flip()
negation_words <- c("not", "no", "never", "without")
AFINN <- get_sentiments("afinn")
negated_words <- bigrams_separated %>%
  filter(word1 %in% negation_words) %>%
  inner_join(AFINN, by = c(word2 = "word")) %>%
  count(word1, word2, value, sort = TRUE)
negated_words
negated_words %>% select(word1) %>% distinct()
negated_words %>%
  mutate(word1 = factor(word1, levels = unique(word1))) %>% 
  group_by(word1) %>%
  top_n(12) %>%
  ungroup() %>%
  mutate(contribution = n * value) %>%
  mutate(word2 = reorder_within(word2, contribution, word1)) %>%
  ggplot(aes(word2, contribution, fill = contribution > 0)) +
  geom_col(show.legend = FALSE) +
  xlab("Words preceded by \"not\"") +
  ylab("Sentiment value * number of occurrences") +
  facet_wrap(~ word1, ncol = 2, scales = "free", dir = "v") +
  coord_flip() + 
  scale_x_reordered()

20/04/2020

suppressMessages({
  if(!require(topicmodels))
    install.packages("topicmodels", repos = "http://cran.us.r-project.org")
  library(topicmodels)
  
})


data("AssociatedPress")
AssociatedPress
# set a seed so that the output of the model is predictable
ap_lda <- LDA(AssociatedPress, k = 2, control = list(seed = 1234))
ap_lda
suppressMessages({
  if(!require(tidytext))
    install.packages("tidytext", repos = "http://cran.us.r-project.org")
  library(tidytext)
})


ap_topics <- tidy(ap_lda, matrix = "beta")
ap_topics

ap_top_terms <- ap_topics %>% group_by(topic) %>% top_n(10, beta) %>% ungroup() %>% arrange(topic, -beta)

ap_top_terms %>% mutate(term = reorder_within(term, beta, topic)) %>% ggplot(aes(term, beta, fill = factor(topic))) + geom_col(show.legend = FALSE) + facet_wrap(~ topic, scales = “free”) + coord_flip() + scale_x_reordered()

library(ggplot2)
library(dplyr)

ap_top_terms <- ap_topics %>%
  group_by(topic) %>%
  top_n(10,beta) %>%
  ungroup() %>%
  arrange(topic, -beta)
ap_top_terms

ap_top_terms %>%
  mutate(term = reorder_within(term, beta, topic)) %>%
  ggplot(aes(term, beta, fill = factor(topic))) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~ topic, scales = "free") +
  coord_flip() +
  scale_x_reordered()
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KMDgvMDUvMjAyMA0KDQpgYGB7cn0NCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHRpZHl0ZXh0KQ0KbGlicmFyeShyZWFkdGV4dCkNCmxpYnJhcnkobWFncml0dHIpDQpsaWJyYXJ5KHRpZHlyKQ0KDQpgYGANCg0KYGBge3J9DQp3ZCA8LSBnZXR3ZCgpDQpwYXRoMSA8LSBmaWxlLnBhdGgod2QsICJIb21vIERldXNfIEEgQnJpZWYgSGlzdG9yeSBvZiBUIC0gWXV2YWwgTm9haCBIYXJhcmkudHh0IikNCnBhdGgyIDwtIGZpbGUucGF0aCh3ZCwiU2FwaWVuc18gQSBCcmllZiBIaXN0b3J5IG9mIEh1bSAtIFl1dmFsIE5vYWggSGFyYXJpLnR4dCIpDQpwYXRoMyA8LSBmaWxlLnBhdGgod2QsICIyMSBMZXNzb25zIGZvciB0aGUgMjFzdCBDZW50dXJ5IC0gWXV2YWwgTm9haCBIYXJhcmkudHh0IikNCg0KaG9tbyA8LSByZWFkdGV4dChwYXRoMSkgIA0Kc2FwaWVucyA8LSByZWFkdGV4dChwYXRoMikNCmxlc3NvbnMgPC0gcmVhZHRleHQocGF0aDMpDQoNCnl1dmFsX2Jvb2tzIDwtIGJpbmRfcm93cyhob21vLCBzYXBpZW5zLCBsZXNzb25zKQ0KeXV2YWxfYm9va3MgJTw+JSBtdXRhdGUoYm9vayA9IGNhc2Vfd2hlbihkb2NfaWQgPT0gIkhvbW8gRGV1c18gQSBCcmllZiBIaXN0b3J5IG9mIFQgLSBZdXZhbCBOb2FoIEhhcmFyaS50eHQiIH4gImhvbW9fZHVlcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvY19pZCA9PSAiU2FwaWVuc18gQSBCcmllZiBIaXN0b3J5IG9mIEh1bSAtIFl1dmFsIE5vYWggSGFyYXJpLnR4dCIgfiAic2FwaWVucyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvY19pZCA9PSAiMjEgTGVzc29ucyBmb3IgdGhlIDIxc3QgQ2VudHVyeSAtIFl1dmFsIE5vYWggSGFyYXJpLnR4dCIgfiAiMjFfbGVzc29ucyIpKSAlPiUNCiAgc2VsZWN0KC1kb2NfaWQpDQogIA0KDQpoZWFkKHl1dmFsX2Jvb2tzKQ0KDQpgYGANCg0KYGBge3J9DQp5dXZhbF9iaWdyYW0gPC0geXV2YWxfYm9va3MgJT4lIHVubmVzdF90b2tlbnMoYmlncmFtLCB0ZXh0LCB0b2tlbiA9ICJuZ3JhbXMiLCBuID0gMikNCmBgYA0KDQpgYGB7cn0NCmhlYWQoeXV2YWxfYmlncmFtKQ0KdGFpbCh5dXZhbF9iaWdyYW0pDQpgYGANCg0KYGBge3J9DQp5dXZhbF9iaWdyYW0gJT4lIGNvdW50KGJpZ3JhbSwgc29ydCA9IFRSVUUpDQpgYGANCg0KYGBge3J9DQpiaWdyYW1fc2VwYXJhdGVkIDwtIHl1dmFsX2JpZ3JhbSAlPiUgc2VwYXJhdGUoYmlncmFtLCBjKCJ3b3JkMSIsICJ3b3JkMiIpLCBzZXAgPSAiICIpDQoNCmJpZ3JhbV9maWx0ZXJlZCA8LSBiaWdyYW1fc2VwYXJhdGVkICU+JSBmaWx0ZXIoIXdvcmQxICVpbiUgc3RvcF93b3JkcyR3b3JkKSAlPiUNCiAgZmlsdGVyKCF3b3JkMiAlaW4lIHN0b3Bfd29yZHMkd29yZCkNCg0KYmlncmFtX2NvdW50ZWQgPC0gYmlncmFtX2ZpbHRlcmVkICU+JQ0KICBjb3VudCh3b3JkMSwgd29yZDIsIHNvcnQgPSBUUlVFKQ0KYmlncmFtX2NvdW50ZWQNCmBgYA0KDQpgYGB7cn0NCmJpZ3JhbV91bml0ZWQgPC0gYmlncmFtX2ZpbHRlcmVkICU+JSB1bml0ZShiaWdyYW0sIHdvcmQxLCB3b3JkMiwgc2VwID0gIiAiKQ0KYmlncmFtX3VuaXRlZA0KYGBgDQoNCmBgYHtyfQ0KdHJpZ3JhbV9maWx0ZXJlZCA8LSB5dXZhbF9ib29rcyAlPiUNCiAgdW5uZXN0X3Rva2Vucyh0cmlncmFtLCB0ZXh0LCB0b2tlbiA9ICJuZ3JhbXMiLCBuID0gMykgJT4lDQogIHNlcGFyYXRlKHRyaWdyYW0sIGMoIndvcmQxIiwgIndvcmQyIiwgIndvcmQzIiksIHNlcCA9ICIgIikgJT4lDQogIGZpbHRlcighd29yZDEgJWluJSBzdG9wX3dvcmRzJHdvcmQsDQogICAgICAgICAhd29yZDIgJWluJSBzdG9wX3dvcmRzJHdvcmQsDQogICAgICAgICAhd29yZDMgJWluJSBzdG9wX3dvcmRzJHdvcmQpDQoNCnRyaWdyYW1fZmlsdGVyZWQgJT4lIGNvdW50KHdvcmQxLCB3b3JkMiwgd29yZDMsIHNvcnQgPSBUUlVFKQ0KDQp0cmlncmFtX3VuaXRlZCA8LSB0cmlncmFtX2ZpbHRlcmVkICU+JSB1bml0ZSh0cmlncmFtLCB3b3JkMSwgd29yZDIsIHdvcmQzLCBzZXAgPSAiICIpDQoNCnRyaWdyYW1fdW5pdGVkDQpgYGANCg0KYGBge3J9DQp0cmlncmFtX2ZpbHRlcmVkICU+JQ0KICBmaWx0ZXIod29yZDIgPT0gImZ1dHVyZSIpICU+JQ0KICBjb3VudChib29rLCB3b3JkMSwgc29ydCA9IFRSVUUpDQpgYGANCg0KYGBge3J9DQp0cmlncmFtX2ZpbHRlcmVkICU+JQ0KICBmaWx0ZXIod29yZDIgPT0gInByZXNlbnQiKSAlPiUNCiAgY291bnQoYm9vaywgd29yZDEsIHNvcnQgPSBUUlVFKQ0KYGBgDQoNCmBgYHtyfQ0KbW9ub2dyYW1fZmlsdGVyZWQgPC0geXV2YWxfYm9va3MgJT4lDQogIHVubmVzdF90b2tlbnMobW9ub2dyYW0sIHRleHQsIHRva2VuID0gIm5ncmFtcyIsIG4gPSAxKSAlPiUNCiAgZmlsdGVyKCFtb25vZ3JhbSAlaW4lIHN0b3Bfd29yZHMkd29yZCkNCmBgYA0KDQpgYGB7cn0NCm1vbm9ncmFtX2ZpbHRlcmVkICU+JQ0KICBmaWx0ZXIobW9ub2dyYW0gPT0gImZ1dHVyZSIpICU+JQ0KICBjb3VudChib29rLCBtb25vZ3JhbSwgc29ydCA9IFRSVUUpDQpgYGANCg0KYGBge3J9DQpiaWdyYW1fdGZfaWRmIDwtIGJpZ3JhbV91bml0ZWQgJT4lDQogIGNvdW50KGJvb2ssIGJpZ3JhbSkgJT4lDQogIGJpbmRfdGZfaWRmKGJpZ3JhbSwgYm9vaywgbikgJT4lDQogIGFycmFuZ2UoZGVzYyh0Zl9pZGYpKQ0KDQpiaWdyYW1fdGZfaWRmDQpgYGANCg0KYGBge3J9DQp0cmlncmFtX3RmX2lkZiA8LSB0cmlncmFtX3VuaXRlZCAlPiUNCiAgY291bnQoYm9vaywgdHJpZ3JhbSkgJT4lDQogIGJpbmRfdGZfaWRmKHRyaWdyYW0sIGJvb2ssIG4pICU+JQ0KICBhcnJhbmdlKGRlc2ModGZfaWRmKSkNCg0KdHJpZ3JhbV90Zl9pZGYNCmBgYA0KDQpgYGB7cn0NCmJpZ3JhbV9zZXBhcmF0ZWQgJT4lDQogIGZpbHRlcih3b3JkMSA9PSAibm90IikgJT4lDQogIGNvdW50KHdvcmQxLCB3b3JkMiwgc29ydCA9IFRSVUUpDQpgYGANCmBgYHtyfQ0KQUZJTk4gPC0gZ2V0X3NlbnRpbWVudHMoImFmaW5uIikNCg0KQUZJTk4NCmBgYA0KDQpgYGB7cn0NCm5vdF93b3JkcyA8LSBiaWdyYW1fc2VwYXJhdGVkICU+JQ0KICBmaWx0ZXIod29yZDEgPT0gIm5vdCIpICU+JQ0KICBpbm5lcl9qb2luKEFGSU5OLCBieSA9IGMod29yZDIgPSAid29yZCIpKSAlPiUNCiAgY291bnQod29yZDIsIHZhbHVlLCBzb3J0ID0gVFJVRSkNCg0Kbm90X3dvcmRzDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQoNCm5vdF93b3JkcyAlPiUNCiAgbXV0YXRlKGNvbnRyaWJ1dGlvbiA9IG4gKiB2YWx1ZSkgJT4lDQogIGFycmFuZ2UoZGVzYyhhYnMoY29udHJpYnV0aW9uKSkpICU+JQ0KICBoZWFkKDIwKSAlPiUNCiAgbXV0YXRlKHdvcmQyID0gcmVvcmRlcih3b3JkMiwgY29udHJpYnV0aW9uKSkgJT4lDQogIGdncGxvdChhZXMod29yZDIsIG4gKiB2YWx1ZSwgZmlsbCA9IG4gKiB2YWx1ZSA+IDApKSArDQogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgeGxhYigiV29yZHMgcHJlY2VkZWQgYnkgXCJub3RcIiIpICsNCiAgeWxhYigiU2VudGltZW50IHZhbHVlICogbnVtYmVyIG9mIG9jY3VycmVuY2VzIikgKw0KICBjb29yZF9mbGlwKCkNCmBgYA0KDQpgYGB7cn0NCm5lZ2F0aW9uX3dvcmRzIDwtIGMoIm5vdCIsICJubyIsICJuZXZlciIsICJ3aXRob3V0IikNCg0KbmVnYXRlZF93b3JkcyA8LSBiaWdyYW1fc2VwYXJhdGVkICU+JQ0KICBmaWx0ZXIod29yZDEgJWluJSBuZWdhdGlvbl93b3JkcykgJT4lDQogIGlubmVyX2pvaW4oQUZJTk4sIGJ5ID0gYyh3b3JkMiA9ICJ3b3JkIikpICU+JQ0KICBjb3VudCh3b3JkMSwgd29yZDIsIHZhbHVlLCBzb3J0ID0gVFJVRSkNCg0KbmVnYXRlZF93b3Jkcw0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShpZ3JhcGgpDQoNCiMgb3JpZ2luYWwgY291bnRzDQpiaWdyYW1fY291bnRlZA0KYGBgDQoNCmBgYHtyfQ0KIyBmaWx0ZXIgZm9yIG9ubHkgcmVsYXRpdmVseSBjb21tb24gY29tYmluYXRpb25zDQpiaWdyYW1fZ3JhcGggPC0gYmlncmFtX2NvdW50ZWQgJT4lDQogIGZpbHRlcihuID4gMjApICU+JQ0KICBncmFwaF9mcm9tX2RhdGFfZnJhbWUoKQ0KDQpiaWdyYW1fZ3JhcGgNCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkoZ2dyYXBoKQ0Kc2V0LnNlZWQoMjAxNykNCg0KZ2dyYXBoKGJpZ3JhbV9ncmFwaCwgbGF5b3V0ID0gImZyIikgKw0KICBnZW9tX2VkZ2VfbGluaygpICsNCiAgZ2VvbV9ub2RlX3BvaW50KCkgKw0KICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwgdmp1c3QgPSAxLCBoanVzdCA9IDEpDQpgYGANCg0KYGBge3J9DQpzZXQuc2VlZCgyMDE2KQ0KDQphIDwtIGdyaWQ6OmFycm93KHR5cGUgPSAiY2xvc2VkIiwgbGVuZ3RoID0gdW5pdCguMTUsICJpbmNoZXMiKSkNCg0KZ2dyYXBoKGJpZ3JhbV9ncmFwaCwgbGF5b3V0ID0gImZyIikgKw0KICBnZW9tX2VkZ2VfbGluayhhZXMoZWRnZV9hbHBoYSA9IG4pLCBzaG93LmxlZ2VuZCA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICBhcnJvdyA9IGEsIGVuZF9jYXAgPSBjaXJjbGUoLjA3LCAnaW5jaGVzJykpICsNCiAgZ2VvbV9ub2RlX3BvaW50KGNvbG9yID0gImxpZ2h0Ymx1ZSIsIHNpemUgPSA1KSArDQogIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbCA9IG5hbWUpLCB2anVzdCA9IDEsIGhqdXN0ID0gMSkgKw0KICB0aGVtZV92b2lkKCkNCmBgYA0KDQotLS0tLQ0KMjkvMDQvMjAyMCAtIDA3LzA1LzIwMjANCg0KYGBge3J9DQoNCmBgYA0KDQoNCmBgYHtyfQ0KbGlicmFyeShxdWFudGVkYSkNCmxpYnJhcnkocmVhZHRleHQpDQpsaWJyYXJ5KHRpZHl0ZXh0KQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpgYGANCg0KDQoNCmBgYHtyfQ0KI3BhdGgxIDwtICJDOlxcVXNlcnNcXEU3NDcwXFxDYWxpYnJlIExpYnJhcnlcXFl1dmFsIE5vYWggSGFyYXJpXFxIb21vIERldXNfIEEgQnJpZWYgSGlzdG9yeSBvZiBUb21vciAoNjYzKVxcSG9tbyBEZXVzXyBBIEJyaWVmIEhpc3Rvcnkgb2YgVCAtIFl1dmFsIE5vYWggSGFyYXJpLnR4dCINCiNwYXRoMiA8LSAiQzpcXFVzZXJzXFxFNzQ3MFxcQ2FsaWJyZSBMaWJyYXJ5XFxZdXZhbCBOb2FoIEhhcmFyaVxcU2FwaWVuc18gQSBCcmllZiBIaXN0b3J5IG9mIEh1bWFua2kgKDM1MylcXFNhcGllbnNfIEEgQnJpZWYgSGlzdG9yeSBvZiBIdW0gLSBZdXZhbCBOb2FoIEhhcmFyaS50eHQiDQojcGF0aDMgPC0gIkM6XFxVc2Vyc1xcRTc0NzBcXENhbGlicmUgTGlicmFyeVxcWXV2YWwgTm9haCBIYXJhcmlcXDIxIExlc3NvbnMgZm9yIHRoZSAyMXN0IENlbnR1cnkgKDY2NClcXDIxIExlc3NvbnMgZm9yIHRoZSAyMXN0IENlbnR1cnkgLSBZdXZhbCBOb2FoIEhhcmFyaS50eHQiDQoNCnBhdGgxIDwtICJDOlxcVHJhbiBQaHUgSG9hIHdvcmtzcGFjZVxcMS4gQ3VycmVudFxcMV9OTFBcXE5hdHVyYWwtTGFuZ3VhZ2UtUHJvY2Vzc2luZ1xcSG9tbyBEZXVzXyBBIEJyaWVmIEhpc3Rvcnkgb2YgVCAtIFl1dmFsIE5vYWggSGFyYXJpLnR4dCINCnBhdGgyIDwtICJDOlxcVHJhbiBQaHUgSG9hIHdvcmtzcGFjZVxcMS4gQ3VycmVudFxcMV9OTFBcXE5hdHVyYWwtTGFuZ3VhZ2UtUHJvY2Vzc2luZ1xcU2FwaWVuc18gQSBCcmllZiBIaXN0b3J5IG9mIEh1bSAtIFl1dmFsIE5vYWggSGFyYXJpLnR4dCINCnBhdGgzIDwtICJDOlxcVHJhbiBQaHUgSG9hIHdvcmtzcGFjZVxcMS4gQ3VycmVudFxcMV9OTFBcXE5hdHVyYWwtTGFuZ3VhZ2UtUHJvY2Vzc2luZ1xcMjEgTGVzc29ucyBmb3IgdGhlIDIxc3QgQ2VudHVyeSAtIFl1dmFsIE5vYWggSGFyYXJpLnR4dCINCg0KaG9tb19kZXVzIDwtIHJlYWR0ZXh0KHBhdGgxKSAgDQpzYXBpZW5zIDwtIHJlYWR0ZXh0KHBhdGgyKQ0KbGVzc29ucyA8LSByZWFkdGV4dChwYXRoMykNCg0KeXV2YWxfYm9va3MgPC0gYmluZF9yb3dzKGhvbW9fZGV1cywgc2FwaWVucywgbGVzc29ucykNCg0KDQpgYGANCg0KYGBge3J9DQp0aWR5X3l1dmFsIDwtIHl1dmFsX2Jvb2tzICU+JSB1bm5lc3RfdG9rZW5zKHdvcmQsdGV4dCkNCmBgYA0KDQpgYGB7cn0NCnRpZHlfeXV2YWwgICU+JSANCiAgICBhbnRpX2pvaW4oc3RvcF93b3JkcykgJT4lIA0KICAgIGNvdW50KGRvY19pZCx3b3JkLCBzb3J0ID0gVFJVRSkgJT4lDQogICAgYmluZF90Zl9pZGYod29yZCxkb2NfaWQsbikgJT4lIA0KICAgIHNlbGVjdCgtZG9jX2lkKSAlPiUNCiAgICBhcnJhbmdlKGRlc2ModGZfaWRmKSkNCmBgYA0KDQpgYGB7cn0NCiNteXN0b3B3b3JkcyA8LSB0aWJibGUod29yZCA9IGMoInd3dy7DoiIsICIyMDE3IiwgIjIwMTYiLCAiY29ydMOjIiwgIjIwMTUiLCAiYm4iLCAiZmlsZSIsICJjZyIsICJjYiIsICJjbSIsICJhYiIsICJfayIsICJfa18iLCAiX3giKSkNCg0KI3BoeXNpY3Nfd29yZHMgPC0gYW50aV9qb2luKHBoeXNpY3Nfd29yZHMsIG15c3RvcHdvcmRzLCBieSA9ICJ3b3JkIikNCg0KcGxvdF95dXZhbCA8LSB0aWR5X3l1dmFsICU+JSANCiAgYW50aV9qb2luKHN0b3Bfd29yZHMpICU+JSANCiAgY291bnQoZG9jX2lkLHdvcmQsIHNvcnQgPSBUUlVFKSAlPiUNCiAgYmluZF90Zl9pZGYod29yZCxkb2NfaWQsbikgJT4lDQogIG11dGF0ZSh3b3JkID0gc3RyX3JlbW92ZV9hbGwod29yZCwgIl8iKSkgJT4lDQogIGdyb3VwX2J5KGRvY19pZCkgJT4lIA0KICB0b3BfbigxNSwgdGZfaWRmKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBtdXRhdGUod29yZCA9IHJlb3JkZXJfd2l0aGluKHdvcmQsIHRmX2lkZiwgZG9jX2lkKSkgJT4lDQogIG11dGF0ZShhdXRob3IgPSBmYWN0b3IoZG9jX2lkLCBsZXZlbHMgPSBjKCJIb21vIERldXNfIEEgQnJpZWYgSGlzdG9yeSBvZiBUIC0gWXV2YWwgTm9haCBIYXJhcmkudHh0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjIxIExlc3NvbnMgZm9yIHRoZSAyMXN0IENlbnR1cnkgLSBZdXZhbCBOb2FoIEhhcmFyaS50eHQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU2FwaWVuc18gQSBCcmllZiBIaXN0b3J5IG9mIEh1bSAtIFl1dmFsIE5vYWggSGFyYXJpLnR4dCIpKSkNCg0KZ2dwbG90KHBsb3RfeXV2YWwsIGFlcyh3b3JkLCB0Zl9pZGYsIGZpbGwgPSBkb2NfaWQpKSArDQogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgbGFicyh4ID0gTlVMTCwgeSA9ICJ0Zi1pZGYiKSArDQogIGZhY2V0X3dyYXAofmRvY19pZCwgbmNvbCA9IDIsIHNjYWxlcyA9ICJmcmVlIikgKw0KICBjb29yZF9mbGlwKCkgKw0KICBzY2FsZV94X3Jlb3JkZXJlZCgpDQpgYGANCg0KYGBge3J9DQp5dXZhbF9ib29rcyAlPiUgDQogIGZpbHRlcihzdHJfZGV0ZWN0KHRleHQsICIyMDE3IikpICU+JSANCiAgc2VsZWN0KHRleHQpDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KGZvcmNhdHMpDQoNCnBsb3RfcGh5c2ljcyA8LSBwaHlzaWNzX3dvcmRzICU+JQ0KICBiaW5kX3RmX2lkZih3b3JkLCBhdXRob3IsIG4pICU+JQ0KICBtdXRhdGUod29yZCA9IGZjdF9yZW9yZGVyKHdvcmQsIHRmX2lkZikpICU+JQ0KICBtdXRhdGUoYXV0aG9yID0gZmFjdG9yKGF1dGhvciwgbGV2ZWxzID0gYygiR2FsaWxlaSwgR2FsaWxlbyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJIdXlnZW5zLCBDaHJpc3RpYWFuIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUZXNsYSwgTmlrb2xhIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkVpbnN0ZWluLCBBbGJlcnQiKSkpDQpgYGANCg0KYGBge3J9DQoNCnBsb3RfcGh5c2ljcyAlPiUgDQogIGdyb3VwX2J5KGF1dGhvcikgJT4lIA0KICB0b3BfbigxNSwgdGZfaWRmKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUNCiAgbXV0YXRlKHdvcmQgPSByZW9yZGVyKHdvcmQsIHRmX2lkZikpICU+JQ0KICBnZ3Bsb3QoYWVzKHdvcmQsIHRmX2lkZiwgZmlsbCA9IGF1dGhvcikpICsNCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICBsYWJzKHggPSBOVUxMLCB5ID0gInRmLWlkZiIpICsNCiAgZmFjZXRfd3JhcCh+YXV0aG9yLCBuY29sID0gMiwgc2NhbGVzID0gImZyZWUiKSArDQogIGNvb3JkX2ZsaXAoKQ0KYGBgDQoNCi0tLS0tLQ0KMjgvMDQvMjAyMA0KDQpDaGFwdGVyIDMgDQpXZSB3YW50IHRvIGFuc3dlciBob3cgdG8gcXVhbnRpZnkgd2hhdCBhIGRvY3VtZW50IGlzIGFib3V0DQoNClRvIGFuc3dlciB0aGVzZSBxdWVzdGlvbnMgd2UgdGVzdCB0aGlzIG1ldGhvZDoNCnRmID0gdGVybSBmcmVxdWVuY3kNCmlkZiA9IGludmVyc2UgZG9jdW1lbnQgZnJlcXVlbmN5ID0gZGVjcmVhc2VzIHRoZSB3ZWlnaHQgZm9yIGNvbW1vbmx5IHVzZWQgd29yZHMgYW5kIGluY3JlYXNlcyB0aGUgd2VpZ2h0IGZvciB3b3JkcyB0aGF0IGFyZSBub3QgdXNlZCB2ZXJ5IG11Y2ggaW4gYSBjb2xsZWN0aW9uIG9mIGRvY3VtZW50cw0KDQp0ZiAtIGlkZiA9IFRoZSBzdGF0aXN0aWMgdGYtaWRmIGlzIGludGVuZGVkIHRvIG1lYXN1cmUgaG93IGltcG9ydGFudCBhIHdvcmQgaXMgdG8gYSBkb2N1bWVudCBpbiBhIGNvbGxlY3Rpb24gKG9yIGNvcnB1cykgb2YgZG9jdW1lbnRzLCBmb3IgZXhhbXBsZSwgdG8gb25lIG5vdmVsIGluIGEgY29sbGVjdGlvbiBvZiBub3ZlbHMgb3IgdG8gb25lIHdlYnNpdGUgaW4gYSBjb2xsZWN0aW9uIG9mIHdlYnNpdGVzLg0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShqYW5lYXVzdGVucikNCmxpYnJhcnkodGlkeXRleHQpDQpsaWJyYXJ5KHRleHRkYXRhKQ0KDQpib29rX3dvcmRzIDwtIGF1c3Rlbl9ib29rcygpICU+JQ0KICB1bm5lc3RfdG9rZW5zKHdvcmQsIHRleHQpICU+JQ0KICBjb3VudChib29rLCB3b3JkLCBzb3J0ID0gVFJVRSkNCg0KdG90YWxfd29yZHMgPC0gYm9va193b3JkcyAlPiUgDQogIGdyb3VwX2J5KGJvb2spICU+JSANCiAgc3VtbWFyaXplKHRvdGFsID0gc3VtKG4pKQ0KDQpib29rX3dvcmRzIDwtIGxlZnRfam9pbihib29rX3dvcmRzLCB0b3RhbF93b3JkcykNCmJvb2tfd29yZHMNCmBgYA0KDQpaaXBm4oCZcyBsYXcgc3RhdGVzIHRoYXQgdGhlIGZyZXF1ZW5jeSB0aGF0IGEgd29yZCBhcHBlYXJzIGlzIGludmVyc2VseSBwcm9wb3J0aW9uYWwgdG8gaXRzIHJhbmsuDQoNCmBgYHtyfQ0KZnJlcV9ieV9yYW5rIDwtIGJvb2tfd29yZHMgJT4lIA0KICBncm91cF9ieShib29rKSAlPiUgDQogIG11dGF0ZShyYW5rID0gcm93X251bWJlcigpLCANCiAgICAgICAgIGB0ZXJtIGZyZXF1ZW5jeWAgPSBuL3RvdGFsKQ0KDQpmcmVxX2J5X3JhbmsNCmBgYA0KDQpgYGB7cn0NCmZyZXFfYnlfcmFuayAlPiUgDQogIGdncGxvdChhZXMocmFuaywgYHRlcm0gZnJlcXVlbmN5YCwgY29sb3IgPSBib29rKSkgKyANCiAgZ2VvbV9saW5lKHNpemUgPSAxLjEsIGFscGhhID0gMC44LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIA0KICBzY2FsZV94X2xvZzEwKCkgKw0KICBzY2FsZV95X2xvZzEwKCkNCg0KYGBgDQoNCmBgYHtyfQ0KcmFua19zdWJzZXQgPC0gZnJlcV9ieV9yYW5rICU+JSANCiAgZmlsdGVyKHJhbmsgPCA1MDAsDQogICAgICAgICByYW5rID4gMTApDQoNCmxtKGxvZzEwKGB0ZXJtIGZyZXF1ZW5jeWApIH4gbG9nMTAocmFuayksIGRhdGEgPSByYW5rX3N1YnNldCkNCmBgYA0KDQoNCmBgYHtyfQ0KZnJlcV9ieV9yYW5rICU+JSANCiAgZ2dwbG90KGFlcyhyYW5rLCBgdGVybSBmcmVxdWVuY3lgLCBjb2xvciA9IGJvb2spKSArIA0KICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAtMC42Miwgc2xvcGUgPSAtMS4xLCBjb2xvciA9ICJncmF5NTAiLCBsaW5ldHlwZSA9IDIpICsNCiAgZ2VvbV9saW5lKHNpemUgPSAxLjEsIGFscGhhID0gMC44LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIA0KICBzY2FsZV94X2xvZzEwKCkgKw0KICBzY2FsZV95X2xvZzEwKCkNCmBgYA0KDQoNClRoZSBpZGVhIG9mIHRmLWlkZiBpcyB0byBmaW5kIHRoZSBpbXBvcnRhbnQgd29yZHMgZm9yIHRoZSBjb250ZW50IG9mIGVhY2ggZG9jdW1lbnQgYnkgZGVjcmVhc2luZyB0aGUgd2VpZ2h0IGZvciBjb21tb25seSB1c2VkIHdvcmRzIGFuZCBpbmNyZWFzaW5nIHRoZSB3ZWlnaHQgZm9yIHdvcmRzIHRoYXQgYXJlIG5vdCB1c2VkIHZlcnkgbXVjaCBpbiBhIGNvbGxlY3Rpb24gb3IgY29ycHVzIG9mIGRvY3VtZW50cywgaW4gdGhpcyBjYXNlLCB0aGUgZ3JvdXAgb2YgSmFuZSBBdXN0ZW7igJlzIG5vdmVscyBhcyBhIHdob2xlLiBDYWxjdWxhdGluZyB0Zi1pZGYgYXR0ZW1wdHMgdG8gZmluZCB0aGUgd29yZHMgdGhhdCBhcmUgaW1wb3J0YW50IChpLmUuLCBjb21tb24pIGluIGEgdGV4dCwgYnV0IG5vdCB0b28gY29tbW9uDQoNCmBgYHtyfQ0KYm9va193b3JkcyA8LSBib29rX3dvcmRzICU+JQ0KICBiaW5kX3RmX2lkZih3b3JkLCBib29rLCBuKQ0KDQpib29rX3dvcmRzDQpgYGANCg0KYGBge3J9DQpib29rX3dvcmRzICU+JQ0KICBzZWxlY3QoLXRvdGFsKSAlPiUNCiAgYXJyYW5nZShkZXNjKHRmX2lkZikpDQpgYGANCmBgYHtyfQ0KYm9va193b3JkcyAlPiUNCiAgYXJyYW5nZShkZXNjKHRmX2lkZikpICU+JQ0KICBtdXRhdGUod29yZCA9IGZhY3Rvcih3b3JkLCBsZXZlbHMgPSByZXYodW5pcXVlKHdvcmQpKSkpDQpgYGANCg0KDQpgYGB7cn0NCmJvb2tfd29yZHMgJT4lDQogIGFycmFuZ2UoZGVzYyh0Zl9pZGYpKSAlPiUNCiAgbXV0YXRlKHdvcmQgPSBmYWN0b3Iod29yZCwgbGV2ZWxzID0gcmV2KHVuaXF1ZSh3b3JkKSkpKSAlPiUgDQogIGdyb3VwX2J5KGJvb2spICU+JSANCiAgdG9wX24oMTUpICU+JSANCiAgdW5ncm91cCgpDQpgYGANCg0KYGBge3J9DQpib29rX3dvcmRzICU+JQ0KICBhcnJhbmdlKGRlc2ModGZfaWRmKSkgJT4lDQogIG11dGF0ZSh3b3JkID0gZmFjdG9yKHdvcmQsIGxldmVscyA9IHJldih1bmlxdWUod29yZCkpKSkgJT4lIA0KICBncm91cF9ieShib29rKSAlPiUgDQogIHRvcF9uKDE1KSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUNCiAgZ2dwbG90KGFlcyh3b3JkLCB0Zl9pZGYsIGZpbGwgPSBib29rKSkgKw0KICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogIGxhYnMoeCA9IE5VTEwsIHkgPSAidGYtaWRmIikgKw0KICBmYWNldF93cmFwKH5ib29rLCBuY29sID0gMiwgc2NhbGVzID0gImZyZWUiKSArDQogIGNvb3JkX2ZsaXAoKQ0KYGBgDQpTdGlsbCBhbGwgcHJvcGVyIG5vdW5zIGluIEZpZ3VyZSAzLjQhIFRoZXNlIHdvcmRzIGFyZSwgYXMgbWVhc3VyZWQgYnkgdGYtaWRmLCB0aGUgbW9zdCBpbXBvcnRhbnQgdG8gZWFjaCBub3ZlbCBhbmQgbW9zdCByZWFkZXJzIHdvdWxkIGxpa2VseSBhZ3JlZS4gV2hhdCBtZWFzdXJpbmcgdGYtaWRmIGhhcyBkb25lIGhlcmUgaXMgc2hvdyB1cyB0aGF0IEphbmUgQXVzdGVuIHVzZWQgc2ltaWxhciBsYW5ndWFnZSBhY3Jvc3MgaGVyIHNpeCBub3ZlbHMsIGFuZCB3aGF0IGRpc3Rpbmd1aXNoZXMgb25lIG5vdmVsIGZyb20gdGhlIHJlc3Qgd2l0aGluIHRoZSBjb2xsZWN0aW9uIG9mIGhlciB3b3JrcyBhcmUgdGhlIHByb3BlciBub3VucywgdGhlIG5hbWVzIG9mIHBlb3BsZSBhbmQgcGxhY2VzLiBUaGlzIGlzIHRoZSBwb2ludCBvZiB0Zi1pZGY7IGl0IGlkZW50aWZpZXMgd29yZHMgdGhhdCBhcmUgaW1wb3J0YW50IHRvIG9uZSBkb2N1bWVudCB3aXRoaW4gYSBjb2xsZWN0aW9uIG9mIGRvY3VtZW50cy4NCg0KDQoNCmBgYHtyfQ0Kc3VwcHJlc3NNZXNzYWdlcyh7DQogIGlmKCFyZXF1aXJlKGd1dGVuYmVyZ3IpKQ0KICAgIGluc3RhbGwucGFja2FnZXMoImd1dGVuYmVyZ3IiLCByZXBvcyA9ICJodHRwOi8vY3Jhbi51cy5yLXByb2plY3Qub3JnIikNCiAgbGlicmFyeShndXRlbmJlcmdyKQ0KICANCn0pDQpwaHlzaWNzIDwtIGd1dGVuYmVyZ19kb3dubG9hZChjKDM3NzI5LCAxNDcyNSwgMTM0NzYsIDMwMTU1KSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRhX2ZpZWxkcyA9ICJhdXRob3IiKQ0KYGBgDQoNCg0KIyBUbyBjYWxjdWxhdGUgYW5kIHZpc3VsaXplIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB3b3JkcyBpbiBteSB0ZXh0IGRhdGFzZXQNClRva2VuaXppbmcgYnkgbi1ncmFtDQoNCldlIGRvIHRoaXMgYnkgYWRkaW5nIHRoZSB0b2tlbiA9ICJuZ3JhbXMiIG9wdGlvbiB0byB1bm5lc3RfdG9rZW5zKCksIGFuZCBzZXR0aW5nIG4gdG8gdGhlIG51bWJlciBvZiB3b3JkcyB3ZSB3aXNoIHRvIGNhcHR1cmUgaW4gZWFjaCBuLWdyYW0uIFdoZW4gd2Ugc2V0IG4gdG8gMiwgd2UgYXJlIGV4YW1pbmluZyBwYWlycyBvZiB0d28gY29uc2VjdXRpdmUgd29yZHMsIG9mdGVuIGNhbGxlZCDigJxiaWdyYW1z4oCdOg0KYGBge3J9DQphdXN0ZW5fYmlncmFtcyA8LSBhdXN0ZW5fYm9va3MoKSAlPiUNCiAgdW5uZXN0X3Rva2VucyhiaWdyYW0sIHRleHQsIHRva2VuID0gIm5ncmFtcyIsIG4gPSAyKQ0KDQphdXN0ZW5fYmlncmFtcw0KYGBgDQoNCiBDb3VudGluZyBhbmQgZmlsdGVyaW5nIG4tZ3JhbXMNCmBgYHtyfQ0KYXVzdGVuX2JpZ3JhbXMgJT4lDQogIGNvdW50KGJpZ3JhbSwgc29ydCA9IFRSVUUpDQpgYGANCg0KQXMgb25lIG1pZ2h0IGV4cGVjdCwgYSBsb3Qgb2YgdGhlIG1vc3QgY29tbW9uIGJpZ3JhbXMgYXJlIHBhaXJzIG9mIGNvbW1vbiAodW5pbnRlcmVzdGluZykgd29yZHMsIHN1Y2ggYXMgb2YgdGhlIGFuZCB0byBiZTogd2hhdCB3ZSBjYWxsIOKAnHN0b3Atd29yZHPigJ0gKHNlZSBDaGFwdGVyIDEpLiBUaGlzIGlzIGEgdXNlZnVsIHRpbWUgdG8gdXNlIHRpZHly4oCZcyBzZXBhcmF0ZSgpLCB3aGljaCBzcGxpdHMgYSBjb2x1bW4gaW50byBtdWx0aXBsZSBiYXNlZCBvbiBhIGRlbGltaXRlci4gVGhpcyBsZXRzIHVzIHNlcGFyYXRlIGl0IGludG8gdHdvIGNvbHVtbnMsIOKAnHdvcmQx4oCdIGFuZCDigJx3b3JkMuKAnSwgYXQgd2hpY2ggcG9pbnQgd2UgY2FuIHJlbW92ZSBjYXNlcyB3aGVyZSBlaXRoZXIgaXMgYSBzdG9wLXdvcmQuDQoNCmBgYHtyfQ0KYmlncmFtc19zZXBhcmF0ZWQgPC0gYXVzdGVuX2JpZ3JhbXMgJT4lDQogIHNlcGFyYXRlKGJpZ3JhbSwgYygid29yZDEiLCAid29yZDIiKSwgc2VwID0gIiAiKQ0KDQpiaWdyYW1zX2ZpbHRlcmVkIDwtIGJpZ3JhbXNfc2VwYXJhdGVkICU+JQ0KICBmaWx0ZXIoIXdvcmQxICVpbiUgc3RvcF93b3JkcyR3b3JkKSAlPiUNCiAgZmlsdGVyKCF3b3JkMiAlaW4lIHN0b3Bfd29yZHMkd29yZCkNCg0KIyBuZXcgYmlncmFtIGNvdW50czoNCmJpZ3JhbV9jb3VudHMgPC0gYmlncmFtc19maWx0ZXJlZCAlPiUgDQogIGNvdW50KHdvcmQxLCB3b3JkMiwgc29ydCA9IFRSVUUpDQoNCmJpZ3JhbV9jb3VudHMNCmBgYA0KDQoNCmBgYHtyfQ0KYmlncmFtc191bml0ZWQgPC0gYmlncmFtc19maWx0ZXJlZCAlPiUNCiAgdW5pdGUoYmlncmFtLCB3b3JkMSwgd29yZDIsIHNlcCA9ICIgIikNCg0KYmlncmFtc191bml0ZWQNCmBgYA0KDQpJbiBvdGhlciBhbmFseXNlcyB5b3UgbWF5IGJlIGludGVyZXN0ZWQgaW4gdGhlIG1vc3QgY29tbW9uIHRyaWdyYW1zLCB3aGljaCBhcmUgY29uc2VjdXRpdmUgc2VxdWVuY2VzIG9mIDMgd29yZHMuIFdlIGNhbiBmaW5kIHRoaXMgYnkgc2V0dGluZyBuID0gMzoNCg0KYGBge3J9DQphdXN0ZW5fYm9va3MoKSAlPiUNCiAgdW5uZXN0X3Rva2Vucyh0cmlncmFtLCB0ZXh0LCB0b2tlbiA9ICJuZ3JhbXMiLCBuID0gMykgJT4lDQogIHNlcGFyYXRlKHRyaWdyYW0sIGMoIndvcmQxIiwgIndvcmQyIiwgIndvcmQzIiksIHNlcCA9ICIgIikgJT4lDQogIGZpbHRlcighd29yZDEgJWluJSBzdG9wX3dvcmRzJHdvcmQsDQogICAgICAgICAhd29yZDIgJWluJSBzdG9wX3dvcmRzJHdvcmQsDQogICAgICAgICAhd29yZDMgJWluJSBzdG9wX3dvcmRzJHdvcmQpICU+JQ0KICBjb3VudCh3b3JkMSwgd29yZDIsIHdvcmQzLCBzb3J0ID0gVFJVRSkNCg0KYGBgDQoNCg0KYGBge3J9DQpiaWdyYW1zX2ZpbHRlcmVkICU+JQ0KICBmaWx0ZXIod29yZDIgPT0gInN0cmVldCIpICU+JQ0KICBjb3VudChib29rLCB3b3JkMSwgc29ydCA9IFRSVUUpDQpgYGANCg0KQSBiaWdyYW0gY2FuIGFsc28gYmUgdHJlYXRlZCBhcyBhIHRlcm0gaW4gYSBkb2N1bWVudCBpbiB0aGUgc2FtZSB3YXkgdGhhdCB3ZSB0cmVhdGVkIGluZGl2aWR1YWwgd29yZHMuIEZvciBleGFtcGxlLCB3ZSBjYW4gbG9vayBhdCB0aGUgdGYtaWRmIChDaGFwdGVyIDMpIG9mIGJpZ3JhbXMgYWNyb3NzIEF1c3RlbiBub3ZlbHMuIFRoZXNlIHRmLWlkZiB2YWx1ZXMgY2FuIGJlIHZpc3VhbGl6ZWQgd2l0aGluIGVhY2ggYm9vaywganVzdCBhcyB3ZSBkaWQgZm9yIHdvcmRzIChGaWd1cmUgNC4xKS4NCg0KYGBge3J9DQpiaWdyYW1fdGZfaWRmIDwtIGJpZ3JhbXNfdW5pdGVkICU+JQ0KICBjb3VudChib29rLCBiaWdyYW0pICU+JQ0KICBiaW5kX3RmX2lkZihiaWdyYW0sIGJvb2ssIG4pICU+JQ0KICBhcnJhbmdlKGRlc2ModGZfaWRmKSkNCg0KYmlncmFtX3RmX2lkZg0KYGBgDQoNCmBgYHtyfQ0Kbm90X3dvcmRzIDwtIGJpZ3JhbXNfc2VwYXJhdGVkICU+JQ0KICBmaWx0ZXIod29yZDEgPT0gIm5vdCIpICU+JQ0KICBpbm5lcl9qb2luKEFGSU5OLCBieSA9IGMod29yZDIgPSAid29yZCIpKSAlPiUNCiAgY291bnQod29yZDIsIHZhbHVlLCBzb3J0ID0gVFJVRSkNCg0Kbm90X3dvcmRzDQpgYGANCg0KDQoNCmBgYHtyfQ0Kbm90X3dvcmRzICU+JQ0KICBtdXRhdGUoY29udHJpYnV0aW9uID0gbiAqIHZhbHVlKSAlPiUNCiAgYXJyYW5nZShkZXNjKGFicyhjb250cmlidXRpb24pKSkgJT4lDQogIGhlYWQoMjApICU+JQ0KICBtdXRhdGUod29yZDIgPSByZW9yZGVyKHdvcmQyLCBjb250cmlidXRpb24pKSAlPiUNCiAgZ2dwbG90KGFlcyh3b3JkMiwgbiAqIHZhbHVlLCBmaWxsID0gbiAqIHZhbHVlID4gMCkpICsNCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICB4bGFiKCJXb3JkcyBwcmVjZWRlZCBieSBcIm5vdFwiIikgKw0KICB5bGFiKCJTZW50aW1lbnQgdmFsdWUgKiBudW1iZXIgb2Ygb2NjdXJyZW5jZXMiKSArDQogIGNvb3JkX2ZsaXAoKQ0KYGBgDQoNCmBgYHtyfQ0KbmVnYXRpb25fd29yZHMgPC0gYygibm90IiwgIm5vIiwgIm5ldmVyIiwgIndpdGhvdXQiKQ0KQUZJTk4gPC0gZ2V0X3NlbnRpbWVudHMoImFmaW5uIikNCm5lZ2F0ZWRfd29yZHMgPC0gYmlncmFtc19zZXBhcmF0ZWQgJT4lDQogIGZpbHRlcih3b3JkMSAlaW4lIG5lZ2F0aW9uX3dvcmRzKSAlPiUNCiAgaW5uZXJfam9pbihBRklOTiwgYnkgPSBjKHdvcmQyID0gIndvcmQiKSkgJT4lDQogIGNvdW50KHdvcmQxLCB3b3JkMiwgdmFsdWUsIHNvcnQgPSBUUlVFKQ0KbmVnYXRlZF93b3Jkcw0KYGBgDQpgYGB7cn0NCm5lZ2F0ZWRfd29yZHMgJT4lIHNlbGVjdCh3b3JkMSkgJT4lIGRpc3RpbmN0KCkNCmBgYA0KDQoNCg0KYGBge3J9DQpuZWdhdGVkX3dvcmRzICU+JQ0KICBtdXRhdGUod29yZDEgPSBmYWN0b3Iod29yZDEsIGxldmVscyA9IHVuaXF1ZSh3b3JkMSkpKSAlPiUgDQogIGdyb3VwX2J5KHdvcmQxKSAlPiUNCiAgdG9wX24oMTIpICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIG11dGF0ZShjb250cmlidXRpb24gPSBuICogdmFsdWUpICU+JQ0KICBtdXRhdGUod29yZDIgPSByZW9yZGVyX3dpdGhpbih3b3JkMiwgY29udHJpYnV0aW9uLCB3b3JkMSkpICU+JQ0KICBnZ3Bsb3QoYWVzKHdvcmQyLCBjb250cmlidXRpb24sIGZpbGwgPSBjb250cmlidXRpb24gPiAwKSkgKw0KICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogIHhsYWIoIldvcmRzIHByZWNlZGVkIGJ5IFwibm90XCIiKSArDQogIHlsYWIoIlNlbnRpbWVudCB2YWx1ZSAqIG51bWJlciBvZiBvY2N1cnJlbmNlcyIpICsNCiAgZmFjZXRfd3JhcCh+IHdvcmQxLCBuY29sID0gMiwgc2NhbGVzID0gImZyZWUiLCBkaXIgPSAidiIpICsNCiAgY29vcmRfZmxpcCgpICsgDQogIHNjYWxlX3hfcmVvcmRlcmVkKCkNCmBgYA0KDQoNCi0tLS0tLS0tLQ0KMjAvMDQvMjAyMA0KDQpgYGB7cn0NCnN1cHByZXNzTWVzc2FnZXMoew0KICBpZighcmVxdWlyZSh0b3BpY21vZGVscykpDQogICAgaW5zdGFsbC5wYWNrYWdlcygidG9waWNtb2RlbHMiLCByZXBvcyA9ICJodHRwOi8vY3Jhbi51cy5yLXByb2plY3Qub3JnIikNCiAgbGlicmFyeSh0b3BpY21vZGVscykNCiAgDQp9KQ0KDQoNCmRhdGEoIkFzc29jaWF0ZWRQcmVzcyIpDQpBc3NvY2lhdGVkUHJlc3MNCmBgYA0KDQpgYGB7cn0NCiMgc2V0IGEgc2VlZCBzbyB0aGF0IHRoZSBvdXRwdXQgb2YgdGhlIG1vZGVsIGlzIHByZWRpY3RhYmxlDQphcF9sZGEgPC0gTERBKEFzc29jaWF0ZWRQcmVzcywgayA9IDIsIGNvbnRyb2wgPSBsaXN0KHNlZWQgPSAxMjM0KSkNCmFwX2xkYQ0KYGBgDQoNCg0KYGBge3J9DQpzdXBwcmVzc01lc3NhZ2VzKHsNCiAgaWYoIXJlcXVpcmUodGlkeXRleHQpKQ0KICAgIGluc3RhbGwucGFja2FnZXMoInRpZHl0ZXh0IiwgcmVwb3MgPSAiaHR0cDovL2NyYW4udXMuci1wcm9qZWN0Lm9yZyIpDQogIGxpYnJhcnkodGlkeXRleHQpDQp9KQ0KDQoNCmFwX3RvcGljcyA8LSB0aWR5KGFwX2xkYSwgbWF0cml4ID0gImJldGEiKQ0KYXBfdG9waWNzDQpgYGANCg0KYXBfdG9wX3Rlcm1zIDwtIGFwX3RvcGljcyAlPiUNCiAgZ3JvdXBfYnkodG9waWMpICU+JQ0KICB0b3BfbigxMCwgYmV0YSkgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgYXJyYW5nZSh0b3BpYywgLWJldGEpDQoNCmFwX3RvcF90ZXJtcyAlPiUNCiAgbXV0YXRlKHRlcm0gPSByZW9yZGVyX3dpdGhpbih0ZXJtLCBiZXRhLCB0b3BpYykpICU+JQ0KICBnZ3Bsb3QoYWVzKHRlcm0sIGJldGEsIGZpbGwgPSBmYWN0b3IodG9waWMpKSkgKw0KICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogIGZhY2V0X3dyYXAofiB0b3BpYywgc2NhbGVzID0gImZyZWUiKSArDQogIGNvb3JkX2ZsaXAoKSArDQogIHNjYWxlX3hfcmVvcmRlcmVkKCkNCiAgDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZHBseXIpDQoNCmFwX3RvcF90ZXJtcyA8LSBhcF90b3BpY3MgJT4lDQogIGdyb3VwX2J5KHRvcGljKSAlPiUNCiAgdG9wX24oMTAsYmV0YSkgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgYXJyYW5nZSh0b3BpYywgLWJldGEpDQphcF90b3BfdGVybXMNCg0KYXBfdG9wX3Rlcm1zICU+JQ0KICBtdXRhdGUodGVybSA9IHJlb3JkZXJfd2l0aGluKHRlcm0sIGJldGEsIHRvcGljKSkgJT4lDQogIGdncGxvdChhZXModGVybSwgYmV0YSwgZmlsbCA9IGZhY3Rvcih0b3BpYykpKSArDQogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgZmFjZXRfd3JhcCh+IHRvcGljLCBzY2FsZXMgPSAiZnJlZSIpICsNCiAgY29vcmRfZmxpcCgpICsNCiAgc2NhbGVfeF9yZW9yZGVyZWQoKQ0KYGBgDQoNCg0KDQo=